package validator

import (
	"reflect"
	"strings"

	"gitee.com/knowgo/ego/egoc"
)

const (
	tagEgo           = "ego"
	tagOptional      = "optional"   // 用于 loader 中的注入对象可选标记
	tagRequired      = "required"   // 用于参数
	tagDeprecated    = "deprecated" // 用于参数
	tagJson          = "json"
	tagJsonString    = "string"
	tagJsonOmitempty = "omitempty"
	tagDatetime      = string(egoc.TagDatetime)
	tagDate          = string(egoc.TagDate)
	tagTime          = string(egoc.TagTime)
	tagMillinSecond  = string(egoc.TagMillinSecond)
)

type Tag struct {
	// 字段名称,使用 json 定义，如果没有则使用 fieldName
	JsonName string
	// ValueIsString bool   // 数据以字符串形式存储
	// Description 说明，字符串形式，以 ' 符号包括，如果有多个则只取第一个
	Descriptions []string
	// JsonValueIsString json tag 中有 string 标记
	JsonValueIsString bool
	// Required 是否必填, 直接以 required 形式出现，如果没有则为 false
	// 如果是 true 或者提交的值不为空，则要配合 Limits 进行数据验证
	Required bool

	DateTimeType egoc.TimeFormatter // time.Time 的类型

	// Optional 是否为可选注入对象
	Optional bool
	// Limit 限制
	// string:
	//		[1:] 			字符串长度大于等于 1
	//		(1:) 			字符串长度大于 1
	//		[:10] 			字符串长度小于等于 10
	//		[:10) 			字符串长度小于 10
	//		(1:10)			字符串长度大于 1 小于 10
	//		[ab,ba]		值必须是 "ab" 或 "ba"
	//		(ab,ba,'a,b')		值不能是 "ab" 或 "ba" 或 "a,b"
	//		[$正则]		 值必须能通过正则表达式验证
	//		($正则)		 值必须不能通过正则表达式验证
	// time.Time: 时间类型
	//
	// array:
	//		[1:] 			数组长度大于等于 1
	//		... ... 参考 string 数值相关的限制说明
	// map:
	//		[1:] 			map 中的元素数大于等于 1
	//		... ... 参考 string 数值相关的限制说明
	// 数值:
	//		[1:] 			值大于等于 1
	//		[1,2,5,10]		数值等于 1、2、5 或 10
	//		(1,2,5,10)		数值不能等于 1、2、5 或 10
	//		... ... 参考 string 数值相关的限制说明
	// 数值(含数字，字符串长度，数组元素范围)：可以有 [] () (] [) 形式
	// 数值(含数字，字符串长度，数组元素范围)：可以有 [] () (] [) 形式
	// Limits     string
	Validators []Validator
	// Regexp      string // 正则表达式限制
	// Deprecated 弃用标志，deprecated
	Deprecated bool // 建议不要使用该参数
	// Error      string // 设置的错误提示
}

func ParseTag(field *reflect.StructField, errTagReport func(tagItem string)) *Tag {
	var tag Tag
	var tags = field.Tag
	jsonTag := tags.Get(tagJson)
	if jsonTag == "-" {
		// 忽略该字段
		return nil
	}
	name, isString := parseJsonName(jsonTag)
	tag.JsonValueIsString = isString
	if name != "" {
		// 名称不是空
		tag.JsonName = name
	} else {
		// 名称是空
		tag.JsonName = field.Name
	}

	egoTag := tags.Get(tagEgo)
	items := splitEgoTag(egoTag)
	for _, item := range items {
		// item = strings.TrimSpace(item)
		if item == tagRequired {
			tag.Required = true
			// tag.Validators = append([]Validator{&Required{}}, tag.Validators...)
			continue
		}
		if item == tagDatetime && tag.DateTimeType == "" {
			tag.DateTimeType = egoc.TagDatetime
		}
		if item == tagDate && tag.DateTimeType == "" {
			tag.DateTimeType = egoc.TagDate
		}
		if item == tagTime && tag.DateTimeType == "" {
			tag.DateTimeType = egoc.TagTime
		}
		if item == tagMillinSecond && tag.DateTimeType == "" {
			tag.DateTimeType = egoc.TagMillinSecond
		}
		if item == tagDeprecated {
			tag.Deprecated = true
			continue
		}
		if item == tagOptional {
			tag.Optional = true
			continue
		}
		fch := item[0]
		if fch == '(' || fch == '[' {
			lch := item[len(item)-1]
			if lch != ']' && lch != ')' {
				// 错误的 tag 定义
				if errTagReport != nil {
					errTagReport(item)
				}
				continue
				// return nil, fmt.Errorf("invalid tag: %s", item)
			}
			// 解析 validator
			va := NewValidator(field.Type, item)
			if va == nil {
				if errTagReport != nil {
					errTagReport(item)
				}
				continue
			}
			// 追加 validator
			tag.Validators = append(tag.Validators, va)
			continue
		}
		if fch == '\'' {
			lch := item[len(item)-1]
			if lch != '\'' {
				// 错误的 tag 定义
				if errTagReport != nil {
					errTagReport(item)
				}
				continue
				// return nil, fmt.Errorf("invalid tag: %s", item)
			}
			if len(item) <= 2 {
				continue
			}
			item = item[1 : len(item)-2]
		}
		tag.Descriptions = append(tag.Descriptions, item)
	}
	// fmt.Println(items)
	// fmt.Println(tag)

	return &tag
}

func splitEgoTag(tag string) []string {
	// var pos = 0
	var items []string
	var state = 0
	var waitFor map[rune]struct{}
	var tmp string
	var lastCh rune
	var hasFoundWaitChar = false
	for _, ch := range tag {
		switch state {
		case 0:
			if tmp != "" {
				tmp = strings.TrimSpace(tmp)
				if tmp != "" {
					items = append(items, tmp)
				}
				tmp = ""
			}
			if ch == ',' || ch == ' ' || ch == '\t' {
				// 忽略
				continue
			}
			if ch == '\'' { // 分析说明信息
				waitFor = map[rune]struct{}{'\'': {}}
			} else if ch == '[' || ch == '(' { // 分析限制信息
				waitFor = map[rune]struct{}{']': {}, ')': {}}
			} else {
				waitFor = nil
			}
			state = 1
			hasFoundWaitChar = false
			tmp += string(ch)
			lastCh = ch
		case 1:
			if ch == ',' {
				if waitFor != nil && !hasFoundWaitChar {
					if _, ok := waitFor[lastCh]; ok {
						// 上一个是结束符
						// tmp += string(ch)
						lastCh = ch
						state = 0
						continue
					}
				} else {
					// tmp += string(ch)
					lastCh = ch
					state = 0
					continue
				}
			}
			if _, ok := waitFor[ch]; ok {
				// 已经遇到结束符
				hasFoundWaitChar = true
			}
			tmp += string(ch)
			lastCh = ch
		}

		// ch := tag[pos]
	}
	if tmp != "" {
		tmp = strings.TrimSpace(tmp)
		if tmp != "" {
			items = append(items, tmp)
		}
	}

	return items
}

func parseJsonName(tag string) (string, bool) {
	ary := strings.Split(tag, ",")
	if len(ary) == 1 {
		return tag, false
	}
	var name string
	// 增加 ,string 检查，实现数据类型转换
	dataTypeIsString := false
	for _, item := range ary {
		if item == tagJsonString {
			dataTypeIsString = true
		} else if item != tagJsonOmitempty && name == "" {
			name = item
		}
	}

	return name, dataTypeIsString
}
